// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	10/9/2014 11:59:44 AM				
// -----------------------------

#include "Application.h"

#include "Resources.h"
#include "EmbeddedResources.h"

#include "SystemInfo.h"

#include "Game.h"
#include "Timer.h"
#include "Window.h"
#include "FileManager.h"
#include "InputManager.h"
#include "AudioManager.h"
#include "RenderManager.h"
#include "Scene.h"

Application::Application()
	: m_lpGame(nullptr)
	, m_lpTimer(nullptr)
	, m_lpWindow(nullptr)
	, m_lpFileManager(nullptr)
	, m_lpInputManager(nullptr)
	, m_lpAudioManager(nullptr)
	, m_lpRenderManager(nullptr)
	, m_lpScene(nullptr)
{
}

Application::~Application()
{
	delete m_lpRenderManager;
	delete m_lpAudioManager;
	delete m_lpInputManager;
	delete m_lpWindow;
	delete m_lpTimer;
	delete m_lpGame;
	
	m_lpFileManager->Clean();
	delete m_lpFileManager;

	Log::Close();
}

Bool Application::Init()
{
	// Open the Log
	Log::Open(_T("Procgen.log"));

	// Write some initial info
	Log::WriteDashedLine();
	Log::WriteLine(_T("[INFO]\t\tProcgen"));
	// Configuration
	TStringStream configStream;
	#ifdef _DEBUG
		configStream << _T("Debug");
	#else
		configStream << _T("Release");
	#endif
	#ifdef _WIN64
		configStream << _T(" x64");
	#else
		configStream << _T(" x86");
	#endif
	Log::WriteLine(_T("[INFO]\t\tBuild Configuration: %s"), configStream.str().c_str());

	// Set the Seed
	UInt32 uSeed = Math::SetSeed(0);
	Log::WriteLine(_T("[INFO]\t\tRandom Generator Seed: %u"), uSeed);
	Log::WriteDashedLine();

	// Create the FileManager
	Log::WriteLine(_T("[INFO]\t\tCreating File Manager"));
	m_lpFileManager = new FileManager();
	if (!m_lpFileManager->Init())
		return false;

	// Load the Engine Libraries
	Log::WriteLine(_T("[INFO]\t\tUnpacking Libraries"));
	for (Library library : g_Libraries)
		m_lpFileManager->UnpackLibrary(library.first, library.second);

	// Load the Engine Resources
	Log::WriteLine(_T("[INFO]\t\tUnpacking Resources"));
	for (Resource resource : g_Resources)
		m_lpFileManager->UnpackResource(get<0>(resource), get<1>(resource), get<2>(resource));

	// Create & Init the Game
	GameConfig config = {};
	m_lpGame = new Game();
	Log::WriteLine(_T("[INFO]\t\tInitializing Game"));
	m_lpGame->OnInit(this, config);

	// Create the Timer
	Log::WriteLine(_T("[INFO]\t\tCreating Timer"));
	m_lpTimer = new Timer();

	// Create & Init the Window
	Log::WriteLine(_T("[INFO]\t\tCreating Window"));
	m_lpWindow = new Window(this);
	if (!m_lpWindow->Init(config))
		return false;

	// Create the InputManager
	Log::WriteLine(_T("[INFO]\t\tCreating Input Manager"));
	m_lpInputManager = new InputManager();

	// Create the AudioManager
	Log::WriteLine(_T("[INFO]\t\tCreating Audio Manager"));
	m_lpAudioManager = new AudioManager();
	if (!m_lpAudioManager->Init(this))
		return false;

	// Create & Init the RenderManager
	Log::WriteLine(_T("[INFO]\t\tCreating Render Manager"));
	m_lpRenderManager = new RenderManager();
	if (!m_lpRenderManager->Init(this, config.VSync))
		return false;

	Log::WriteDashedLine();

	return true;
}
Int32 Application::Run()
{
	Log::WriteLineSeperator();
	Log::WriteDashedLine();

	// Start the Game
	Log::WriteLine(_T("[INFO]\t\tStarting Game."));
	m_lpGame->OnStart(this);

	// Run the Loop
	while (m_lpWindow->IsAlive())
	{
		// If we do not have to handle messages
		if (!m_lpWindow->HandleMessages())
		{
			// If the Window is active
			// Run a frame
			if (m_lpWindow->IsActive())
			{
				// Update the Timer
				m_lpTimer->Update();
				Float deltaTime = m_lpTimer->GetDeltaTime();

				// Update the InputManager
				m_lpInputManager->Update(this);

				// Update the AudioManager
				m_lpAudioManager->Update(this);

				if (!m_lpScene)
					continue;

				// Update the Game
				m_lpGame->OnUpdate(this, deltaTime);
				// Update the Scene
				m_lpScene->Update(this, deltaTime);

				// Render the Game
				m_lpGame->OnRender(this);

				// Flush the RenderManager
				m_lpRenderManager->Flush(this, m_lpScene);

			}
			// Else give back the time slice
			// to the OS
			else
				Sleep(0);
		}
	}

	// End the Game
	Log::WriteLine(_T("[INFO]\t\tEnding Game."));
	m_lpGame->OnEnd(this);

	Log::WriteDashedLine();

	return 0;
}
void Application::Quit()
{
	Log::WriteLine(_T("[INFO]\t\tSending Destroy message to Window."));
	SendMessage(m_lpWindow->GetHandle(), WM_DESTROY, 0, 0);
}

void Application::EnableCursor(Bool bEnable) const
{
	ShowCursor((bEnable) ? TRUE : FALSE);
}
void Application::ToggleFullscreen()
{
	Bool bIsFullscreen = m_lpRenderManager->GetDevice()->GetFullscreenState();
	m_lpWindow->m_IsFullscreen = !bIsFullscreen;
	m_lpRenderManager->GetDevice()->SetFullscreenState(true);
}

Game* Application::GetGame() const
{
	return m_lpGame;
}
Timer* Application::GetTimer() const
{
	return m_lpTimer;
}
Window* Application::GetWindow() const
{
	return m_lpWindow;
}
FileManager* Application::GetFiles() const
{
	return m_lpFileManager;
}
InputManager* Application::GetInput() const
{
	return m_lpInputManager;
}
AudioManager* Application::GetAudio() const
{
	return m_lpAudioManager;
}
RenderManager* Application::GetRenderer() const
{
	return m_lpRenderManager;
}

void Application::SetScene(Scene* lpScene)
{
	if (m_lpScene)
	{
		Log::WriteLine(_T("[SCENE]\t\tDeactivating Scene \"%s\"."), m_lpScene->GetName().c_str());
		m_lpScene->OnDeactivate(this);
	}
	m_lpScene = lpScene;
	if (m_lpScene)
	{
		Log::WriteLine(_T("[SCENE]\t\tActivating Scene \"%s\"."), m_lpScene->GetName().c_str());
		m_lpScene->OnActivate(this);
	}
}
Scene* Application::GetScene() const
{
	return m_lpScene;
}